home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume8 / uutty / part02 < prev    next >
Encoding:
Internet Message Format  |  1987-02-18  |  40.9 KB

  1. Subject:  v08i073:  Bidirectional getty/login for SystemV, Part02/02
  2. Newsgroups: mod.sources
  3. Approved: mirror!rs
  4.  
  5. Submitted by: mit-eddie!cdx39!jc (John Chambers)
  6. Mod.sources: Volume 8, Issue 73
  7. Archive-name: uutty/Part02
  8.  
  9. [  Sorry for the mixed-mode address on the previous article...  --r$  ]
  10.  
  11. : This is a shar archive. Extract with sh, not csh
  12. echo file: lastfield.c
  13. cat > lastfield.c << '\!Funky\!Stuff\!'
  14. /*
  15. ** Find the last field of a string.
  16. */
  17. char *
  18. lastfield(p,c)
  19.     char *p;    /* Null-terminated string to scan */
  20.     int   c;    /* Separator char, usually '/' */
  21. {    char *r;
  22.  
  23.     r = p;
  24.     while (*p)            /* Find the last field of the name */
  25.         if (*p++ == c)
  26.             r = p;
  27.     return r;
  28. }
  29. \!Funky\!Stuff\!
  30. echo file: lockname.c
  31. cat > lockname.c << '\!Funky\!Stuff\!'
  32. #include "uutty.h"
  33. /*
  34. ** Build name for a lockfile.
  35. */
  36. lockname()
  37. {    char *dp, *lp, *rp;
  38.  
  39.     D5("lockname(\"%s\")",device);
  40.     devfld = dp = lastfield(device,'/');
  41.     D4("devfld=\"%s\" device=\"%s\"",devfld,device);
  42.     lp = lockfile;        /* Place to build lockfile name */
  43.     rp = lockroot;        /* Place to build lockfile name */
  44.     while (*rp) *lp++ = *rp++;    /* Copy root to lockname */
  45.     while (*dp) *lp++ = *dp++;    /* Append the device name */
  46.     *lp = 0;
  47.     D4("lockname:lockfile=\"%s\"",lockfile);
  48. }
  49. \!Funky\!Stuff\!
  50. echo file: lockup.c
  51. cat > lockup.c << '\!Funky\!Stuff\!'
  52. #include "uutty.h"
  53. /* 
  54. ** The lockfile exists; hang until it disappears.
  55. */
  56. lockup()
  57. {    int n;
  58.  
  59.     n = 0;
  60.     do {
  61.         if ((n==0 && debug>=2) || debug>=3)
  62.             P("%s Lockfile \"%s\" exists.",getime(),lockfile);
  63.         if (lsleep > 0)
  64.             sleep(lsleep);
  65.         ++n;
  66.         }
  67.             while (stat(lockfile,&status) >= 0);
  68.     D3("locked: Lockfile \"%s\" freed.",lockfile);
  69.     if (debug>=2)
  70.         P("%s Lockfile \"%s\" gone.",getime(),lockfile);
  71.     return n;
  72. }
  73. \!Funky\!Stuff\!
  74. echo file: lockwait.c
  75. cat > lockwait.c << '\!Funky\!Stuff\!'
  76. #include "uutty.h"
  77. /*
  78. ** Wait on a lockfile.
  79. */
  80. lockwait()
  81. {    unsigned n;
  82.  
  83.     D5("lockwait()");
  84.     if (lockfile[0] == 0) lockname();
  85.     D4("lockwait:lockfile=\"%s\"",lockfile);
  86.     n = 0;
  87.     if (stat(lockfile,&status) >= 0) {
  88.         D4("%s Lockfile \"%s\" exists.",getime(),lockfile);
  89.         n = lockup();
  90.         D4("%s Lockfile \"%s\" gone.",getime(),lockfile);
  91.     }
  92.     if (n) {                /* Port may be screwed up */
  93.         opendev();            /* Close and reopen it */
  94.         restdev();            /* Get it into proper state */
  95.     }
  96.     D4("lockwait:Returned after %d waits.",n);
  97.     return n;
  98. }
  99. \!Funky\!Stuff\!
  100. echo file: main.c
  101. cat > main.c << '\!Funky\!Stuff\!'
  102. #include "uutty.h"
  103. #include <signal.h>
  104. static char id[] = "@(#)RELEASE: 1.0  Sep 20 1986 ~jc/uutty";
  105. /*
  106. **    uutty device [option]...
  107. **
  108. ** This is John Chambers' own personal serial-port daemon, 
  109. ** to watch a port, determine what sort of critter is at the 
  110. ** other end of the line, and engage it in a semi-intelligent 
  111. ** dialog.  The primary goal of this version, uutty, is to
  112. ** make it possible to use direct links (null modems) freely in
  113. ** both directions, without the problems caused by the getty(1)
  114. ** program.  In particular, it allows the use of the uucp package
  115. ** (including uux and cu) in both directions across a line.
  116. **
  117. ** Another thing uutty is good for is using a modem in either
  118. ** without needing any special actions.
  119. **
  120. ** Uutty is a thinly-disguised "state machine" whose basic 
  121. ** behavior is determined by a global "state variable" ss
  122. ** and a string of input characters.  The state variable is
  123. ** simply an integer that encodes the last action performed.  
  124. ** An input routine is called, and when it returns, the input
  125. ** is examined to determine the program's next action.  Normally, 
  126. ** this action is to produce an output string, change the state 
  127. ** variable, and wait for the next input.
  128. **
  129. ** The major intent of this program is to do a login interview,
  130. ** but to ignore anyone at the other end of the line that acts
  131. ** like it's trying to log us in.  If getty or this program is
  132. ** at the other end, the result will be silence until someone
  133. ** sends a CR or LF (which elicits "login:") or something that
  134. ** is acceptable as a login id.  Note in particular that input
  135. ** containing colons and spaces will be ignored.
  136. **
  137. ** The other major intent is to cooperate with any software that
  138. ** sets the uucp lockfiles ("/usr/spool/uucp/LCK..<device>"), by
  139. ** checking for such lockfiles frequently, and sleeping any time
  140. ** they are found.  This program never creates a lockfile; it just
  141. ** sleeps when someone else creates one.  Thus, this program may
  142. ** be left running on a port used by uucp, cu, or other such serial-
  143. ** port users.  There is a slight possibility that this program
  144. ** may eat up part of the first response from the port, but that
  145. ** is rarely much of a problem.  One or two CRs will usually suffice
  146. ** to elicit a "gin:" prompt from the other end.
  147. **
  148. ** A third, minor intent is to recognize overly-intelligent modems,
  149. ** and refuse to become engaged in a conversations with them.  This
  150. ** is done primarily by rejecting input that contains whitespace or
  151. ** special characters, or which contains only upper-case letters.
  152. ** This will handle the error messages produced by most commercial
  153. ** modems.  However, users may wish to add to the code that examines
  154. ** login ids and passwords, and add more checks for specific modems.
  155. **
  156. ** You may notice that this program is full of Dx(...) debug calls.
  157. ** These are defined in dbg.h, and may be suppressed fairly easily.
  158. ** Most C compilers will react to, for instance:
  159. **     #define D9 if(0) dmsg
  160. ** by generating no code at all.  You might think this would make
  161. ** your program smaller and faster, and you'd be partly right.  It
  162. ** will definitely be smaller, but probably not much faster.  Anyhow,
  163. ** the speed of this program isn't much of an issue; if it gets into
  164. ** a feedback loop with a modem, you don't much care how efficiently
  165. ** it brings your system to its knees!  It's probably a wiser idea
  166. ** to leave most of the diagnostic junk in.  You'll be surprised at
  167. ** how useful it can be to have that stuff in an audit trail.
  168. **
  169. ** Some of the options recognized are:
  170. **
  171. ** -b<n>    use a baud rate of <n>.  Final "00" may be omitted.
  172. **
  173. ** -c<n>    slow down by counting to <n> between output chunks.
  174. **
  175. ** -d<n>    debug level <n>; default is -d1.
  176. **
  177. ** -e<s>    exit string <s> to send when terminating.
  178. **
  179. ** -f       fork a subprocess for starting shells.
  180. **
  181. ** -i<s>    initialization string <s> is sent whenever it is
  182. **          decided that the other end is insane or jabbering.
  183. **
  184. ** -l       create uucp lockfiles before starting a shell; delete 
  185. **          them afterwards.  This option implies the -f option.
  186. **
  187. ** -n<s>    nudge string <s> is sent when this program wants a response.
  188. **
  189. ** -p<f>    port to use, where <f> is usually /dev/tty??.  This is
  190. **            the same as using <f> without any prefix.
  191. **
  192. ** -s<n>    sleep <n> seconds between output chunks.
  193. **
  194. ** -x<n>    debug level <n>.
  195. **
  196. ** To be responsible, I should repeat the warning stated elsewhere:
  197. ** with debug level -d2 or higher, uutty writes everything it sees,
  198. ** including unencrypted login ids and passwords, to its diagnostic
  199. ** output stream (audit trail).  This makes it a Trojan Horse of the
  200. ** first kind.  If you are security-conscious, you might take steps
  201. ** to hide the audit trail, and purge it frequently.
  202. **
  203. ** In any case, the audit trail grows without bounds, so you will want
  204. ** to take steps to keep it within bounds.  The easiest way is with an
  205. ** entry in crontab that starts up a daily cleanup script (/etc/cleanup
  206. ** is a real good name for it).  This script can move the uutty audit 
  207. ** trails to a different name, to produce one level of backup.
  208. */
  209. main(ac,av)
  210.     char**av;
  211. {    int i, r;
  212.  
  213.     time(&currtime);        /* Note the start time */
  214.     progname = av[0];        /* For debug output */
  215.     prgnam = lastfield(progname,'/');
  216.     D4("prgnam=\"%s\" progname=\"%s\"",prgnam,progname);
  217.     euid = geteuid();
  218.     egid = getegid();
  219.     ruid = getuid();
  220.     rgid = getgid();
  221.     D3("euid=%d egid=%d ruid=%d rgid=%d",euid,egid,ruid,rgid);
  222.     args(ac,av);            /* Process command-line args */
  223.     if (debug) {            /* Generate first entries in audit trail */
  224.         P("+--------------------------------------------------------------------+");
  225.         P("%s %s started.",getime(),progname);
  226.     }
  227.     timeout = 255;            /* Use max timeout on reads */
  228.     sig();                /* Intercept all the signals */
  229.     slowfl = (count > 0) || (slow > 0);
  230.     opendev();            /* Open the port which we are to use */
  231.     if (dev != 0) {close(0); i = dup(dev); D3("File %d=\"%s\"",i,device);}
  232.     if (dev != 1) {close(1); i = dup(dev); D3("File %d=\"%s\"",i,device);}
  233.     lockwait();            /* First check to see if it's busy */
  234. /*
  235. ** The code to handle raw, 8-bit communication get a bit weird from
  236. ** system to system.  Here, we try to trap the initial state of the
  237. ** port, so that when we die, we can first restore it.
  238. */
  239. #ifdef SYS5
  240.     if (raw && isatty(dev)) {    /* We will want to restore its initial state */
  241.         D5("main:before ioctl(%d,%d,%06lX)",dev,TCGETA,&trminit);
  242.         i = ioctl(dev,TCGETA,&trminit);  /* Note initial state of terminal */
  243.         D5("main: after ioctl(%d,%d,%06lX)=%d",dev,TCGETA,&trminit,i);
  244.         D7("main: %d:\tcflag=%06o",dev,trminit.c_cflag);
  245.         D7("main: %d:\tiflag=%06o",dev,trminit.c_iflag);
  246.         D7("main: %d:\tlflag=%06o",dev,trminit.c_lflag);
  247.         D7("main: %d:\toflag=%06o",dev,trminit.c_oflag);
  248.         termfl = 1;
  249. /*    crlf = "\n\r";            ** Not needed on Unix systems */
  250.     }
  251. #endif
  252.     restdev();            /* Get port to desired (raw, very public) state */
  253. #ifdef SYS5
  254. /*
  255. ** The following functions suffice to find, update, and write
  256. ** the appropriate entry in the /etc/utmp file, which on SYS/V
  257. ** is the record of logged-in users.  This may well have to be
  258. ** changed for other systems.  The hope here is that this is a
  259. ** reasonable way to modularize the job, though the names may
  260. ** not be ideal...
  261. */
  262.     D8("main:before findutmp()");
  263.     up = findutmp();        /* Try to locate our /etc/utmp entry */
  264.     D7("main: after findutmp()=%06lX",up);
  265.     D8("main:before fillutmp(\"%s\",%X,\"%s\",%d)",prgnam,(char*)0,devfld,LOGIN_PROCESS);
  266.     fillutmp(prgnam,(char*)0,devfld,LOGIN_PROCESS);
  267.     D7("main: after fillutmp()");
  268.     D8("pswd:before pututline(%06lX)",up);
  269.     pututline(up);            /* Put modified line into /etc/utmp */
  270.     D7("pswd: after pututline(%06lX)",up);
  271. #endif
  272.     r = talk();            /* Attempt a conversation */
  273.     die(r);            /* If we should ever decide to stop */
  274.     return 0;            /* Paranoia! */
  275. }
  276. \!Funky\!Stuff\!
  277. echo file: makeraw.c
  278. cat > makeraw.c << '\!Funky\!Stuff\!'
  279. /* Tell the device driver to do raw I/O on the device.  
  280.  * Unfortunately, this is done in different ways on different
  281.  * brands of Unix.  Make sure that your system is included
  282.  * somewhere in the following list.
  283.  */
  284. #include <dbg.h>
  285. #ifdef BERK
  286. #    include <sgtty.h>
  287.     int ldisc = NETLDISC;
  288.     struct sgttyb sgttyb;
  289. #endif
  290. #ifdef PCIX
  291. #    include <termio.h>
  292.     struct termio termio;
  293. #endif
  294. #ifdef SYS5
  295. #    include <termio.h>
  296.     struct termio termio;
  297. #endif
  298. extern int baudmask;    /* CBAUD mask, if baud rate specified */
  299. extern int errno;
  300. int  flowcontrol = 1;
  301. uint timeout = 100;    /* Timeout in 0.1 second quanta */
  302.  
  303. makeraw(fn)
  304.     int fn;        /* File number */
  305. {    int i;
  306.  
  307.     D5("makeraw(%d)",fn);
  308.     errno = 0;
  309. #ifdef SYS5
  310.     i = ioctl(fn,TCGETA,&termio);
  311.     D4("makeraw: %d:\tcflag=%06o [old]",fn,termio.c_cflag);
  312.     D4("makeraw: %d:\tiflag=%06o [old]",fn,termio.c_iflag);
  313.     D4("makeraw: %d:\tlflag=%06o [old]",fn,termio.c_lflag);
  314.     D4("makeraw: %d:\toflag=%06o [old]",fn,termio.c_oflag);
  315.     D5("makeraw: ioctl(fn=%d,TCGETA=%d,&termio=%08X)=%d",fn,TCGETA,&termio,i);
  316.     if (baudmask)            /* Wipe out all of cflag but speed */
  317.          termio.c_cflag  = baudmask;
  318.     else termio.c_cflag &= CBAUD;
  319.     termio.c_cflag |= CS8 | CREAD | HUPCL;
  320.     termio.c_iflag  = flowcontrol? (IXON | IXANY | IXOFF): 0;
  321.     termio.c_lflag  = 0;
  322.     termio.c_oflag  = 0;
  323.     termio.c_cc[4]  = 0;        /* Number of bytes to buffer up */
  324.     termio.c_cc[5]  = timeout;    /* Timeout in 0.1 sec units */
  325.     D4("makeraw: %d:\tcflag=%06o [new]",fn,termio.c_cflag);
  326.     D4("makeraw: %d:\tiflag=%06o [new]",fn,termio.c_iflag);
  327.     D4("makeraw: %d:\tlflag=%06o [new]",fn,termio.c_lflag);
  328.     D4("makeraw: %d:\toflag=%06o [new]",fn,termio.c_oflag);
  329.     i = ioctl(fn,TCSETA,&termio);
  330. #endif
  331. #ifdef SYS3
  332.     i = ioctl(fn,TCGETA,&termio);
  333.     termio.c_iflag |=  IGNCR | IXON | IXANY | IXOFF;
  334.     termio.c_lflag  =  0;
  335.     D5("makeraw: \tcflag=%06o",termio.c_cflag);
  336.     D5("makeraw: \tiflag=%06o",termio.c_iflag);
  337.     D5("makeraw: \tlflag=%06o",termio.c_lflag);
  338.     D5("makeraw: \toflag=%06o",termio.c_oflag);
  339.     i = ioctl(fn,TCSETA,&termio);
  340. #endif
  341. #ifdef BERK        /* Berkeley Unix has its own conventions */
  342.     i = ioctl(fn,TIOCGETP,&sgttyb);
  343.     sgttyb.sg_flags  =  RAW | TANDEM;
  344.     ioctl(fn,TIOCSETP,&sgttyb);
  345.     if (errno) {
  346.         E("makeraw: Can't do raw i/o on \"%s\"",fnnam);
  347.         exit(-1);
  348.     }
  349. #endif
  350.     D5("makeraw: %d is now raw",fn);
  351. }
  352. \!Funky\!Stuff\!
  353. echo file: makesane.c
  354. cat > makesane.c << '\!Funky\!Stuff\!'
  355. /* Tell the device driver to do normal I/O on the device.  
  356.  * Unfortunately, this is done in different ways on different
  357.  * brands of Unix.  Make sure that your system is included
  358.  * somewhere in the following list.
  359.  */
  360. #include <dbg.h>
  361. #ifdef BERK
  362. #    include <sgtty.h>
  363.     int ldisc = NETLDISC;
  364.     struct sgttyb sgttyb;
  365. #endif
  366. #ifdef PCIX
  367. #    include <termio.h>
  368.     struct termio termio;
  369. #endif
  370. #ifdef SYS5
  371. #    include <termio.h>
  372. static    struct termio trmstat;
  373. #endif
  374. extern int errno;
  375. extern int baudmask;    /* CBAUD mask if baud rate specified */
  376.  
  377. makesane(fn)
  378.     int fn;        /* File number */
  379. {    int i;
  380.  
  381.     D5("makesane(%d)",fn);
  382.     errno = 0;
  383. #ifdef SYS5
  384.     i = ioctl(fn,TCGETA,&trmstat);
  385.     if (debug >= 4) {
  386.       P("makesane: %d:\tcflag=%06o [old]",fn,trmstat.c_cflag);
  387.       P("makesane: %d:\tiflag=%06o [old]",fn,trmstat.c_iflag);
  388.       P("makesane: %d:\tlflag=%06o [old]",fn,trmstat.c_lflag);
  389.       P("makesane: %d:\toflag=%06o [old]",fn,trmstat.c_oflag);
  390.     }
  391.     D5("makesane: ioctl(fn=%d,TCGETA=%d,&trmstat=%08X)=%d",fn,TCGETA,&trmstat,i);
  392. /* 
  393. ** The following was given by "stty -a" on one SYS/V machine:
  394. **    speed 9600 baud; line = 2; intr = ^c; quit = ^\; erase = ^H; kill = ^x; eof = ^d; eol = ^@;susp = ^z;dsus = ^y
  395. **    -parenb -parodd cs8 -cstopb -hupcl cread -clocal -tostop 
  396. **    -ignbrk brkint ignpar -parmrk -inpck istrip -inlcr -igncr icrnl -iuclc 
  397. **    ixon -ixany -ixoff 
  398. **    isig icanon -xcase echo echoe echok -echonl -noflsh 
  399. **    opost -olcuc onlcr -ocrnl -onocr -onlret -ofill -ofdel tab3 
  400. speed 9600 baud; line = 2; intr = ^c; quit = ^\; erase = DEL; kill = ^x; eof = ^d; eol = ^@;susp = ^z;dsus = ^y
  401. -parenb -parodd cs8 -cstopb -hupcl cread -clocal -tostop 
  402. -ignbrk brkint ignpar -parmrk -inpck istrip -inlcr -igncr icrnl -iuclc 
  403. ixon -ixany -ixoff 
  404. isig icanon -xcase echo echoe echok -echonl -noflsh 
  405. opost -olcuc onlcr -ocrnl -onocr -onlret -ofill -ofdel tab3 
  406. */
  407.     trmstat.c_cflag &= CBAUD;    /* Save the speed */
  408.     if (baudmask) {
  409.       D4("makesane: baudmask=0%o",baudmask);
  410.       trmstat.c_cflag = baudmask;    /* Set a different speed */
  411.     }
  412.     trmstat.c_cflag |= HUPCL  | CREAD  | CS8;
  413.     trmstat.c_iflag  = BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
  414.     trmstat.c_lflag  = ISIG   | ICANON | ECHO   | ECHOE | ECHOK;
  415.     trmstat.c_oflag  = OPOST  | ONLCR  | TAB3;
  416.     trmstat.c_cc[0] = 0x03;    /* INTR = ^C */
  417.     trmstat.c_cc[1] = 0x1C;    /* QUIT = ^\ */
  418.     trmstat.c_cc[2] = 0x10;    /* ERASE= ^H [BS] */
  419.     trmstat.c_cc[3] = 0x18;    /* KILL = ^X */
  420.     trmstat.c_cc[4] = 0x04;    /* EOF  = ^D */
  421.     trmstat.c_cc[5] = 0;    /* EOL  char */
  422.     trmstat.c_cc[6] = 0;    /* EOL2 char */
  423. /*    trmstat.c_cc[7] = __;    ** Reserved */
  424.     trmstat.c_cc[8] = 0x1A;    /* SUSP = ^Z */
  425.     trmstat.c_cc[9] = 0x19;    /* SUSP = ^Y */
  426.     if (debug >= 5)  {
  427.       P("makesane: %d:\tcflag=%06o [new]",fn,trmstat.c_cflag);
  428.       P("makesane: %d:\tiflag=%06o [new]",fn,trmstat.c_iflag);
  429.       P("makesane: %d:\tlflag=%06o [new]",fn,trmstat.c_lflag);
  430.       P("makesane: %d:\toflag=%06o [new]",fn,trmstat.c_oflag);
  431.       Hexdnm(&trmstat.c_cc[0],NCC,"c_cc:");
  432.     }
  433.     i = ioctl(fn,TCSETA,&trmstat);
  434. #endif
  435. #ifdef SYS3        /* Note: Tested only on PC/IX */
  436.     i = ioctl(fn,TCGETA,&trmstat);
  437.     trmstat.c_cflag |= HUPCL  | PARENB | CREAD  | CS7;
  438.     trmstat.c_iflag  = BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
  439.     trmstat.c_lflag  = ISIG   | ICANON | ECHO   | ECHOE | ECHOK;
  440.     trmstat.c_oflag  = OPOST  | ONLCR  | TAB3;
  441.     D5("makesane: \tcflag=%06o",trmstat.c_cflag);
  442.     D5("makesane: \tiflag=%06o",trmstat.c_iflag);
  443.     D5("makesane: \tlflag=%06o",trmstat.c_lflag);
  444.     D5("makesane: \toflag=%06o",trmstat.c_oflag);
  445.     i = ioctl(fn,TCSETA,&trmstat);
  446. #endif
  447. #ifdef BERK        /* Note: Tested only on 4.2 */
  448.     i = ioctl(fn,TIOCGETP,&sgttyb);
  449.     sgttyb.sg_flags  =  SANE | TANDEM;
  450.     ioctl(fn,TIOCSETP,&sgttyb);
  451.     if (errno) {
  452.         E("makesane: Can't do sane i/o on \"%s\"",fnnam);
  453.         exit(-1);
  454.     }
  455. #endif
  456.     D5("makesane: %d is now sane.",fn);
  457. }
  458. \!Funky\!Stuff\!
  459. echo file: nextbyte.c
  460. cat > nextbyte.c << '\!Funky\!Stuff\!'
  461. #include "uutty.h"
  462. /*
  463. ** Extract one byte from the port's input buffer, triggering
  464. ** a read if necessary.  If no data is available within the
  465. ** timeout limit, -1 is returned.  All other return values
  466. ** should be small positive integers, in [1,255].
  467. */
  468. nextbyte()
  469. {    int i;
  470.  
  471.     D9("nextbyte()");
  472. loop:
  473.     if (ibfa >= ibfz) {
  474.         errno = 0;
  475.         lockwait();
  476.         D9("nextbyte:before read(%d,%06lX,%d)",dev,ibuf,IBUF);
  477.         if ((i = read(dev,ibuf,IBUF)) <= 0) {
  478.             D9("nextbyte: read(%d,%06lX,%d)=%d\t[errno=%d]"
  479.                 ,dev,ibuf,IBUF,i,errno);
  480.             Fail;
  481.         }
  482.         D8("nextbyte: after read(%d,%06lX,%d)=%d\t[errno=%d]"
  483.             ,dev,ibuf,IBUF,i,errno);
  484.         if (debug >= 4) {
  485.             dbgtimep = getime();
  486.             Hexdnm(ibuf,i,"Read:");
  487.         }
  488.         if (echoing) {
  489.             write(dev,ibuf,i);
  490.         }
  491.         ibfa = ibuf;
  492.         ibfz = ibuf + i;
  493.         *ibfz = '\0';
  494.     }
  495.     i = ASCII(*ibfa++);
  496.     D9("nextbyte()=%02X='%c'",i,dsp(i));
  497.     if (i == 0) Loop;        /* Don't return nulls */
  498.     return i;
  499. fail:
  500.     D8("nextbyte()=-1 [FAILURE]");
  501.     return -1;
  502. }
  503. \!Funky\!Stuff\!
  504. echo file: opendev.c
  505. cat > opendev.c << '\!Funky\!Stuff\!'
  506. #include "uutty.h"
  507. /*
  508. ** Close and re-open the port.
  509. */
  510. opendev()
  511. {
  512.     D6("opendev()");
  513.     if (dev >= 0) close(dev);
  514.     if (device) {
  515.         if ((dev = open(device,2)) < 0) {
  516.             E("FATAL ERROR\n%s Can't open \"%s\"\t[errno=%d]",getime(),device,errno);
  517.             die(1);
  518.         }
  519.         if (debug) P("%s Opened dev=%d=\"%s\".",getime(),dev,device);
  520.     }
  521.     return dev;
  522. }
  523. \!Funky\!Stuff\!
  524. echo file: option.c
  525. cat > option.c << '\!Funky\!Stuff\!'
  526. #include "uutty.h"
  527. /* 
  528. ** Process an option string.  Note that we get
  529. ** a pointer to the initial char, usually '-'.
  530. */
  531. extern int baudmask;    /* CBAUD mask, if baud rate specified */
  532. extern int baudrate;    /* Actual baud rate */
  533.  
  534. option(op)
  535.     char*op;
  536. {    int  n;
  537.  
  538.     D4("option(\"%s\")",op);
  539.     switch (op[1]) {    /* Which option? */
  540.     default:
  541.         sprintf(stderr,"Unknown option \"%s\" ignored.\n",op);
  542.         break;
  543.     case 'B':        /* Set baud rate */
  544.     case 'b':
  545.         n = sscanf(op+2,"%u",&baudrate);
  546.         if (n < 1) baudrate = 1;
  547.         D3("baudrate=%u\n",baudrate);
  548.         switch (baudrate) {
  549.         case 12: case 1200: baudmask = B1200; break;
  550.         case 24: case 2400: baudmask = B2400; break;
  551.         case 48: case 4800: baudmask = B4800; break;
  552.         case 96: case 9600: baudmask = B9600; break;
  553.         default: E("Can't handle baud rate of %d",baudrate);
  554.             baudrate = baudmask = 0;
  555.         }
  556.         break;
  557.     case 'C':    /* Count between I/O operations */
  558.     case 'c':
  559.         n = sscanf(op+2,"%u",&count);
  560.         if (n < 1) count = 1;
  561.         D3("count=%u\n",count);
  562.         slowfl = count || slow;
  563.         break;
  564.     case 'D':    /* Set debug level */
  565.     case 'd':
  566.     case 'X':    /* Set debug level */
  567.     case 'x':
  568.         n = sscanf(op+2,"%u",&debug);
  569.         if (n < 1) debug = 1;
  570.         D3("debug=%d\n",debug);
  571.         break;
  572.     case 'E':
  573.     case 'e':        /* Exit message */
  574.         if (op[2]) {    /* -e"msg" is exit message */
  575.             m_exit = op + 2;
  576.             D3("m_exit=\"%s\"",m_exit);
  577.         } else {        /* -e turns on echoing */
  578.             echoing = echofl = 1;
  579.             D3("echofl=%d",echofl);
  580.         }
  581.         break;
  582.     case 'F':
  583.     case 'f':       /* Fork subprocesses for a shells */
  584.         forkfl++;
  585.         D3("forkfl=%d",forkfl);
  586.         break;
  587.     case 'H':
  588.     case 'h':    /* Display "help" messages */
  589.         help();
  590.         break;
  591.     case 'I':
  592.     case 'i':       /* Initialization message */
  593.         m_init = op + 2;
  594.         D3("m_init=\"%s\"",m_init);
  595.         break;
  596.     case 'L':
  597.     case 'l':       /* Create lockfile on login */
  598.         lockfl++;
  599.         forkfl++;
  600.         D3("forkfl=%d lockfl=%d",forkfl,lockfl);
  601.         break;
  602.     case 'N':
  603.     case 'n':       /* Nudge message */
  604.         m_nudge = op + 2;
  605.         D3("m_nudge=\"%s\"",m_nudge);
  606.         break;
  607.     case 'P':
  608.     case 'p':       /* Port name */
  609.         device = op + 2;
  610.         D3("device=\"%s\"",device);
  611.         break;
  612.     case 'R':
  613.     case 'r':       /* Raw I/O [default=TRUE] */
  614.         n = sscanf(op+2,"%d",&raw);
  615.         if (n < 1) raw = 1;
  616.         D3("raw=%d\n",raw);
  617.         break;
  618.     case 'S':
  619.     case 's':       /* Slow output, sleep(slow) between buffers */
  620.         n = sscanf(op+2,"%d",&slow);
  621.         if (n < 1) slow = 1;
  622.         D3("slow=%d\n",slow);
  623.         break;
  624.     }
  625.     return 0;
  626. }
  627. \!Funky\!Stuff\!
  628. echo file: pread.c
  629. cat > pread.c << '\!Funky\!Stuff\!'
  630. #include "uutty.h"
  631. /*
  632. ** Read a response from the debugger into a buffer.  Note the three
  633. ** stop conditions: finding an EOL char; filling the buffer; timeout.
  634. */
  635. pread(stopch,bp,bs)
  636.     int  stopch;
  637.     char*bp;
  638.     int  bs;
  639. {    int  c, n;
  640.     uint reads;
  641.  
  642.     D4("pread(stopch=%02X,bp=%06lX,bs=%d)",stopch,bp,bs);
  643.     n = reads = 0;
  644.     while (n < bs) {
  645.         c = nextbyte();        /* Next char from port */
  646.         if (c <= 0) {        /* No data returned? */
  647.             if (debug >= 4)
  648.                 if (reads) P("pread: %d reads",reads);
  649.             if (++reads > l_reads) {
  650.                 errno = ETIMEOUT;    /* Timeout if too many */
  651.                 D3("Input timeout...");
  652.                 Fail;            /* Timeout; return what we have */
  653.             }
  654.             continue;
  655.         }
  656.         D9("pread: c=%02X='%c' bp=%06lX bs=%d",c,dsp(c),bp,bs);
  657.         c &= iomask;        /* Trim it to 7 bits */
  658.         if (c == 0) continue;    /* Ignore nulls */
  659.         *bp++ = c;            /* Note the EOL char is returned */
  660.         ++n;            /* Count the input chars */
  661.         --bs;            /* Decr count of bytes wanted */
  662.         if (c == stopch) Done;    /* Assorted EOL chars */
  663.         switch (c) {
  664.             case 0x03:        /* ^C = ETX */
  665.                 if (debug) P("%s: ^C in input, quitting [id]",getime());
  666.                 die(0);
  667.             case '>' :
  668.             case '\r':
  669.             case '\n':
  670.             case ':' :
  671.             case '%' :
  672.             case 0x04:        /* ^D = EOT  */
  673.             case 0x06:        /* ^D = ACK  */
  674.             case 0x15:        /* ^U = NAK */
  675.                 Done;
  676.         }
  677.         reads = 0;            /* Got data; reset timeout counter */
  678.     }
  679. done:
  680.     *bp = 0;            /* Final null for debugging */
  681. fail:
  682.     return n;
  683. }
  684. \!Funky\!Stuff\!
  685. echo file: pswd.c
  686. cat > pswd.c << '\!Funky\!Stuff\!'
  687. #include "uutty.h"
  688. /* 
  689. ** We have received something that is believed to be a password.
  690. ** It is this routine's job to combine it with the current userid,
  691. ** and determine whether the combination is acceptable.  This
  692. ** routine should work on most Unix systems, but who knows?
  693. */
  694. pswd(rp)
  695.     char *rp;
  696. {    char *p, *q;
  697.     int   i;
  698.     struct passwd *pp;
  699.  
  700.     D4("pswd: r=\"%s\" ss=%d",rp,ss);
  701.     for (p=rp; *p; ++p) {    /* Examine the chars for acceptability */
  702.         switch(*p) {
  703.         case ':':        /* Colons aren't legal */
  704.             D3("Invalid char '%c' in password",*p);
  705.             Fail;
  706.         case '\r':
  707.         case '\n':
  708.             *p = 0;
  709.             goto gotit;
  710.         case '!':        /* Special goodie for killing daemon */
  711.             if (p[1] == 'Q') {
  712.                 if (debug) P("%s: !Q in input, quitting [id]",getime());
  713.                 die(0);
  714.             }
  715.         default:
  716.             continue;
  717.         }
  718.     }
  719. gotit:            /* Make a copy of the supposed id */
  720.     p = rp;
  721.     q = passwd;
  722.     while (*p && q<passwd+PASSWD)
  723.         *q++ = *p++;
  724.     *q = 0;
  725.     if (debug >= 3) P("%s PASSWD=\"%s\"",getime(),passwd);
  726.     D3("userid:\"%s\"",userid);
  727.     D3("pswd:\"%s\"",passwd);
  728.     pp = getpwnam(userid);
  729.     if (pp == 0) {
  730.         D1("Login \"%s\" incorrect.",userid);
  731.         D3("userid \"%s\" not found.",userid);
  732.         Fail;
  733.     }
  734.     if (debug >= 6) Hexdnm(pp,sizeof(*pp),"Passwd:");
  735.     D4("pw_name  =\"%s\"",pp->pw_name);
  736.     D4("pw_passwd=\"%s\"",pp->pw_passwd);
  737.     D4("pw_dir   =\"%s\"",pp->pw_dir);
  738.     D4("pw_shell =\"%s\"",pp->pw_shell);
  739.     D5("pswd:before crypt(\"%s\",\"%s\")",passwd,pp->pw_passwd);
  740.     p = crypt(passwd,pp->pw_passwd);
  741.     D5("pswd: after crypt(\"%s\",\"%s\")=\"%s\"",passwd,pp->pw_passwd,p);
  742.     if (strcmp(p,pp->pw_passwd)) {
  743.         Pwrite("Login incorrect.\r\n");
  744.         D3("pswd \"%s\" not correct.",passwd);
  745.         Fail;
  746.     }
  747.     D3("Login: uid=%d=\"%s\" group=%d accepted.",pp->pw_uid,pp->pw_name,pp->pw_gid);
  748. /*
  749. ** To do the next few changes, we probably need to be a super-user:
  750. */
  751. #ifdef SYS5
  752. /*
  753. ** Attempt to build a utmp structure.
  754. */
  755.     errno = 0;
  756.     up = 0;
  757.     D5("before ttyslot()");
  758.     i = ttyslot();    /* Identify our /etc/utmp line */
  759.     D4("ttyslot()=%d\t[errno=%d]",i,errno);
  760.     findutmp();
  761.     p = 0;
  762.     fillutmp(pp->pw_name,p,devfld,USER_PROCESS);
  763.     D4("pswd:before pututline(%06lX)",up);
  764.     pututline(up);
  765. #endif
  766.     errno = 0;
  767.     i = chmod(device,0644);            /* Restrict terminal access to owner */
  768.     D4("chmod(\"%s\",0%o)=%d\t[errno=%d]",device,0644,i,errno);
  769.     D3("Change \"%s\" to user %d=%s, group %d, permissions 644.",device,pp->pw_uid,pp->pw_name,pp->pw_gid);
  770.     i = chown(device,pp->pw_uid,pp->pw_gid);    /* Change terminal's group */
  771.     D4("chown(\"%s\",%d,%d)=%d",device,pp->pw_uid,pp->pw_gid,i);
  772.     D3("New group %d.",pp->pw_gid);
  773.     i = setgid(pp->pw_gid);            /* Change terminal's owner */
  774.     D4("setgid(%d)=%d",pp->pw_gid,i);
  775.     if (i < 0) Fail; 
  776.     D3("New user  %d.",pp->pw_uid);
  777.     i = setuid(pp->pw_uid);            /* Change to login id */
  778.     D4("setuid(%d)=%d",pp->pw_uid,i);
  779.     if (i < 0) Fail;
  780.     D3("New directory \"%s\"",pp->pw_dir);
  781.     i = chdir(pp->pw_dir);            /* Move to login directory */
  782.     D4("chdir(\"%s\")=%d",pp->pw_dir,i);
  783.     if (i < 0) Fail;
  784. /*
  785. ** Invoke the login shell.
  786. */
  787.     D5("pswd:before exec(1,\"%s\",%lX)",pp->pw_shell,pp);
  788.     exec(1,pp->pw_shell,pp);        /* Start up a shell */
  789.     D5("pswd: after exec(1,\"%s\",%lX)",pp->pw_shell,pp);
  790.     target = "?";                /* We shouldn't get here */
  791. fail:
  792.     D4("pswd(\"%s\") FAILED.",rp);
  793.     if (echofl ) Awrite("\r\nLogin incorrect.");
  794.     if (m_login) {
  795.         Awrite(m_login);
  796.         ss = S_LOGIN;                /* Note login prompt sent */
  797.         D4("State %d=%s",ss,gestate());
  798.     }
  799.     sleep(1);
  800.     return 0;
  801. }
  802. \!Funky\!Stuff\!
  803. echo file: pwrite.c
  804. cat > pwrite.c << '\!Funky\!Stuff\!'
  805. #include "uutty.h"
  806. /* 
  807. ** Write a character string to the port, stopping at the first null.
  808. ** This will be done slowly iff slowfl is turned on.
  809. */
  810. pwrite(msg)
  811.     char*msg;
  812. {    int  i, n;
  813.     char c, *p;
  814.  
  815.     D5("pwrite(%08lX)",msg);
  816.     n = strlen(msg);
  817.     if (debug) {
  818.         dbgtimep = getime();
  819.         if (debug >= 2) Ascdnm(msg,n,"Send:");
  820.         if (debug >= 4) Hexdnm(msg,n,"Send:");
  821.     }
  822.     if (slowfl) {
  823.         D8("port_wr:slow=%d",slow);
  824.         p = msg;
  825.         while (c = *p++) {
  826.             Slowly;
  827.             D9("port_wr:before write(%d,%06lX,%d)",dev,&c,1);
  828.             i = write(dev,&c,1);
  829.             D9("port_wr: after write(%d,%06lX,%d)=%d",dev,&c,1,i);
  830.             if (i <= 0) {
  831.                 if (debug) P("%s: write failed, quitting.",getime());
  832.                 die(2);
  833.             }
  834.         }
  835.     } else {
  836.         D9("port_wr:before write(%d,\"%s\",%d)",dev,msg,n);
  837.         i = write(dev,msg,n);
  838.         D9("port_wr: after write()=%d\t[errno=%d]",i,errno);
  839.     }
  840. }
  841. \!Funky\!Stuff\!
  842. echo file: restdev.c
  843. cat > restdev.c << '\!Funky\!Stuff\!'
  844. #include "uutty.h"
  845. /*
  846. ** Set the device to our desired (raw) state.  This may only 
  847. ** work if we are the super-user.  This routine, such as it is, 
  848. ** should work on just about any Unix system.  See makeraw.c
  849. ** for the real system-dependent stuff.
  850. */
  851. restdev()
  852. {    int i;
  853.  
  854.     D6("restdev()");
  855.     makeraw(dev);            /* We want to do raw I/O */
  856.     errno = 0;
  857.     D2("Change \"%s\" to user %d, group %d, permissions 666.",device,euid,egid);
  858.     i = chown(device,euid,egid);    /* Try to get ownership */
  859.     D4("restdev: chown(\"%s\",%d,%d)=%d",device,euid,egid,i);
  860.     errno = 0;
  861.     i = chmod(device,0666);        /* Make it publicly accessible */
  862.     D4("restdev: chmod(\"%s\",0%o)=%d\t[errno=%d]",device,0666,i,errno);
  863.     return i;
  864. }
  865. \!Funky\!Stuff\!
  866. echo file: resync.c
  867. cat > resync.c << '\!Funky\!Stuff\!'
  868. #include "uutty.h"
  869. /* 
  870. ** Discard buffered input, and try to get us to a start where
  871. ** the next input will be a response to the most recent (or next)
  872. ** output.
  873. */
  874. resync()
  875. {
  876.     if (ibfa < ibfz) {
  877.         dbgtimep = getime();
  878.         if (debug >= 2) Ascdnm(ibfa,ibfz-ibfa,"Drop:");
  879.         if (debug >= 4) Hexdnm(ibfa,ibfz-ibfa,"Drop:");
  880.         restdev();        /* Make sure the device is OK */
  881.         sleep(5);        /* Try not to respond to garbage */
  882.     }
  883.     ibfa = ibfz+1;
  884. }
  885. \!Funky\!Stuff\!
  886. echo file: sendbrk.c
  887. cat > sendbrk.c << '\!Funky\!Stuff\!'
  888. #include "uutty.h"
  889. /* 
  890. ** If fd is a terminal-type device, and ioctl(-,TCSBRK,0) works,
  891. ** this will send a break signal down the line, which may (or may
  892. ** not) get the attention of whatever is at the other end.
  893. */
  894. sendbrk(fd)
  895. {    int i;
  896.  
  897.     D4("BREAK");
  898.     errno = 0;
  899.     D5("sendbrk:before ioctl(fd=%d,TCSBRK=%d,0)",fd,TCSBRK);
  900.     i = ioctl(fd,TCSBRK,0);
  901.     D5("sendbrk: after ioctl(fd=%d,TCSBRK=%d,0)=%d    [errno=%d]",fd,TCSBRK,i,errno);
  902.     if (i<0 || errno)
  903.         D2("ioctl(%d,TCSBRK=%d,0)=%d\t[errno=%d]",fd,TCSBRK,i,errno);
  904.     return i;
  905. }
  906. \!Funky\!Stuff\!
  907. echo file: shprompt.c
  908. cat > shprompt.c << '\!Funky\!Stuff\!'
  909. #include "uutty.h"
  910. /* 
  911. ** We seem to have gotten a shell prompt, but it's kinda hard to be sure.
  912. ** This routine is somewhat of a relic in uutty, but has been left here
  913. ** because you might want to deal with this case.  What we try to do
  914. ** basically is to get the shell to logout.
  915. */
  916. shprompt(rp) char *rp;
  917. {
  918.     D5("shprompt(\"%s\")",rp);
  919.     target = "shell";
  920.     switch(ss) {
  921.     case S_INIT:
  922.     case S_PASSWD:
  923.     default:
  924.         E("****Can't handle shell prompt \"%s\" in state %d=%s.",rp,ss,gestate());
  925.         Awrite("exit\r");        /* This terminates most shells */
  926.         Awrite("logout\r");        /* This terminates other shells */
  927.         Awrite("\4\3");            /* This works with still others */
  928.         if (m_exit) Awrite(m_exit);    /* Other optional exit message */
  929.         if (m_init) Awrite(m_init);    /* Try to tell the modem to quit */
  930.         ss = S_IDLE;
  931.         D4("State %d=%s",ss,gestate());
  932.     }
  933. }
  934. \!Funky\!Stuff\!
  935. echo file: sig.c
  936. cat > sig.c << '\!Funky\!Stuff\!'
  937. #include "uutty.h"
  938. #include <signal.h>
  939.  
  940. sig_1() {P("Signal  1 [SIGHUP]" ); fflush(stdout); die( 1);}
  941. sig_2() {P("Signal  2 [SIGINT]" ); fflush(stdout); die( 2);}
  942. sig_3() {P("Signal  3 [SIGQUIT]"); fflush(stdout); die( 3);}
  943. sig_4() {P("Signal  4 [SIGILL]" ); fflush(stdout); die( 4);}
  944. sig_5() {P("Signal  5 [SIGTRAP]"); fflush(stdout); die( 5);}
  945. sig_6() {P("Signal  6 [SIGIOT]" ); fflush(stdout); die( 6);}
  946. sig_7() {P("Signal  7 [SIGEMT]" ); fflush(stdout); die( 7);}
  947. sig_8() {P("Signal  8 [SIGFPE]" ); fflush(stdout); die( 8);}
  948. sig_9() {P("Signal  9 [SIGKILL]"); fflush(stdout); die( 9);}
  949. sig10() {P("Signal 10 [SIGBUS]" ); fflush(stdout); die(10);}
  950. sig11() {P("Signal 11 [SIGSEGV]"); fflush(stdout); die(11);}
  951. sig12() {P("Signal 12 [SIGSYS]" ); fflush(stdout); die(12);}
  952. sig13() {P("Signal 13 [SIGPIPE]"); fflush(stdout); die(13);}
  953. sig14() {P("Signal 14 [SIGALRM]"); fflush(stdout); die(14);}
  954. sig15() {P("Signal 15 [SIGTERM]"); fflush(stdout); die(15);}
  955. sig16() {P("Signal 16 [SIGADDR]"); fflush(stdout); die(16);}
  956. sig17() {P("Signal 17 [SIGZERO]"); fflush(stdout); die(17);}
  957. sig18() {P("Signal 18 [SIGCHK]" ); fflush(stdout); die(18);}
  958. sig19() {P("Signal 19 [SIGOVER]"); fflush(stdout); die(19);}
  959. sig20() {P("Signal 20 [SIGPRIV]"); fflush(stdout); die(20);}
  960. sig21() {P("Signal 21 [SIGUSR1]"); fflush(stdout); die(21);}
  961. sig22() {P("Signal 22 [SIGUSR2]"); fflush(stdout); die(22);}
  962.  
  963. /* Catch all the signals we can:
  964. */
  965. sig() 
  966. {
  967.   signal( 1,sig_1);
  968.   signal( 2,sig_2);
  969.   signal( 3,sig_3);
  970.   signal( 4,sig_4);
  971.   signal( 5,sig_5);
  972.   signal( 6,sig_6);
  973.   signal( 7,sig_7);
  974.   signal( 8,sig_8);
  975.   signal( 9,sig_9);
  976.   signal(10,sig10);
  977.   signal(11,sig11);
  978.   signal(11,sig11);
  979.   signal(12,sig12);
  980.   signal(13,sig13);
  981.   signal(14,sig14);
  982.   signal(15,sig15);
  983.   signal(16,sig16);
  984.   signal(17,sig17);
  985.   signal(18,sig18);
  986.   signal(19,sig19);
  987.   signal(20,sig20);
  988.   signal(21,sig21);
  989.   signal(22,sig22);
  990. }
  991. \!Funky\!Stuff\!
  992. echo file: slowly.c
  993. cat > slowly.c << '\!Funky\!Stuff\!'
  994. #include "uutty.h"
  995. /* 
  996. ** This routine handles requests to do things slowly.
  997. ** Note the two ways of delaying:  sleeping for "slow"
  998. ** seconds, or counting down from "count" to 1.
  999. */
  1000. slowly()
  1001. {    uint u;
  1002.  
  1003.     D8("slowly() slow=%d count=%d",slow,count);
  1004.     if (slow > 0) {
  1005.         D8("slowly: slow=%d",slow);
  1006.         sleep(slow);
  1007.     }
  1008.     for (u=count; u; u--) ;
  1009.     D8("slowly() done");
  1010. }
  1011. \!Funky\!Stuff\!
  1012. echo file: st.c
  1013. cat > st.c << '\!Funky\!Stuff\!'
  1014. #include "dbg.h"
  1015. /* 
  1016. ** Assorted string-manipulation subroutines.
  1017. */
  1018. /* Match a null-terminated string against the initial portion
  1019. ** of another string.  If the match succeeds, return its length;
  1020. ** else return 0.
  1021. */
  1022. int st_init(x,y)
  1023.     char *x;
  1024.     char *y;
  1025. {    char *s;
  1026.     char *t;
  1027.  
  1028.     s = x;
  1029.     t = y;
  1030.     while (*s)
  1031.         if (*s++ != *t++) goto fail;
  1032.     D9("st_init(\"%s\",\"%s\")=%d",x,y,s-x);
  1033.     return(s - x);
  1034. fail:
  1035.     D9("st_init(\"%s\",\"%s\")=%d",x,y,0);
  1036.     return 0;
  1037. }
  1038. /* Return the int value of the trailing digits of a null-terminated string.
  1039. ** Note that non-numeric chars reset the value, so only digits that follow
  1040. ** all non-numerics are used.  Also, only the last '-' is effective.  Thus
  1041. ** "15-x-37" gives the value 37.
  1042. */    
  1043. int st_ival(s)
  1044.     char *s;
  1045. {    int   c, val, sign;
  1046.  
  1047.     sign = val = 0;
  1048.     while (c = *s++)
  1049.         if ('0'<=c && c<='9')
  1050.             val = (val * 10) + (c - '0');
  1051.         else if (c == '-')
  1052.             sign = c;
  1053.         else 
  1054.             sign = val = 0;
  1055.     return(sign ? -val : val);
  1056. }
  1057. /* Convert a long to an ASCII string.
  1058. ** Return pointer to byte just after the value.
  1059. */
  1060. char * st_ltoa(l,a)
  1061.     long  l;
  1062.     char *a;    
  1063. {
  1064.     if (l < 0) {*a++ = '-'; l = -l;}
  1065.     if (l > 9) a = st_ltoa((l/10),a);
  1066.     *a++ = '0' + (l % 10);
  1067.     return a;
  1068. }
  1069. /* Return the long value of the trailing digits of a null-terminated string.
  1070. ** Note that non-numeric chars reset the value, so only digits that follow
  1071. ** all non-numerics are used.  Also, only the last '-' is effective.  Thus
  1072. ** "15-x-37" gives the value 37.
  1073. */    
  1074. long st_lval(s)
  1075.     char *s;
  1076. {    int   c, sign;
  1077.     long  val;
  1078.  
  1079.     val = sign = 0;
  1080.     D4("st_lval: sign=%d val=%ld",sign,val);
  1081.     while (c = *s++) {
  1082.         if ('0'<=c && c<='9')
  1083.             val = (val * 10) + (c - '0');
  1084.         else if (c == '-')
  1085.             sign = c;
  1086.         else 
  1087.             val = sign = 0;
  1088.         D4("st_lval: sign=%d val=%ld c=%02x='%c'",sign,val,c,dsp(c));
  1089.     }
  1090.     return(sign ? -val : val);
  1091. }    
  1092. \!Funky\!Stuff\!
  1093. echo file: talk.c
  1094. cat > talk.c << '\!Funky\!Stuff\!'
  1095. #include "uutty.h"
  1096. /* 
  1097. ** This routine assumes that it is talking to a port connected
  1098. ** to a VMEbus device with an on-board debugger.  It exchanges
  1099. ** some pleasantries to verify this fact, then procedes to ask
  1100. ** the debugger to show it the requested chunk of its memory.
  1101. */
  1102. talk()
  1103. {    int  c, i, n, r;
  1104.     int  baddies, lowers, uppers;
  1105.     int  messages;
  1106.  
  1107.     D4("talk()");
  1108.     r = 0;
  1109.     ss = S_INIT;
  1110.     D4("State %d=%s",ss,gestate());
  1111.     lockwait();        /* Try to avoid collisions */
  1112.     if (device) target = device;
  1113. /*
  1114. ** We have a long list of initialization strings here.
  1115. ** This has come in handy in a few cases, but usually
  1116. ** they are mostly null.
  1117. */
  1118.     if (m_init )  Awrite(m_init);
  1119.     if (m_init1) {Awrite(m_init1); sleep(SLEEP1);}
  1120.     if (m_init2) {Awrite(m_init2); sleep(SLEEP2);}
  1121.     if (m_init3) {Awrite(m_init3); sleep(SLEEP3);}
  1122.     ss = S_INIT;
  1123.     D4("State %d=%s",ss,gestate());
  1124.     if (m_login)  Awrite(m_login);
  1125.     ss = S_LOGIN;
  1126.     D4("State %d=%s",ss,gestate());
  1127.     Response;
  1128. nudge:            /* Try to elicit a response */
  1129.     D3("Nudge.");
  1130.     if (++nudges > Nudges) {
  1131.         E("Too many nudges.");
  1132.         r = 1;
  1133.         Dead;
  1134.     }
  1135.     lockwait();        /* Try to avoid interference */
  1136.     Resync;
  1137.     if (m_nudge)        /* Is a nudge message defined? */
  1138.         Awrite(m_nudge);    /* If so, send it */
  1139. idle:                    /* We will now accept anything */
  1140.     D3("Idle.");
  1141.     ss = S_IDLE;        /* Note we're awaiting a response */
  1142.     D4("State %d=%s",ss,gestate());
  1143.     messages = 0;        /* Counter to trigger nudges */
  1144. response:                /* Read a response */
  1145.     D4("Response: state=%d=%s",ss,gestate());
  1146.     if (debug > 0 && target != oldtarg) {
  1147.         if (debug >= 3) P("%s Talking to %s.",getime(),target);
  1148.         oldtarg = target;
  1149.     }
  1150.     /*
  1151.     ** Check for any of a list of known debugger prompts.
  1152.     ** If we don't get one of then, try to nudge the debugger,
  1153.     ** and try again, giving up after some number of tries.
  1154.     */
  1155.     errno = 0;
  1156.     D4("talk:l_tries=%d",l_tries);
  1157.     sleep(1);        /* Sheer laziness */
  1158.     lockwait();
  1159.     if (ss != S_PASSWD)    /* If not expecting password, do echoing if requested */
  1160.         echoing = echofl;
  1161.     while ((n = Pread(eol0,rsp,rspmax)) <= 0) {
  1162.         D3("No response.");
  1163.         /*
  1164.         ** Note this program loops forever waiting for something to come down the line.
  1165.         ** You might want to do something else here if there's no input.
  1166.         */
  1167.     }
  1168.     D4("talk:Pread()=%d",n);
  1169.     rsp[n] = 0;
  1170.     ++messages;
  1171.     if (debug) {
  1172.         dbgtimep = getime();
  1173.         if (debug >= 2) Ascdnm(rsp,n,"Got:");
  1174.         if (debug >= 4) Hexdnm(rsp,n,"Got:");
  1175.     }
  1176.     /*
  1177.     ** Next, we do some preliminary checking for sanity of the input.
  1178.     ** Since this program's duty is to do login interviews, the only
  1179.     ** input we really want is something that might be a login id or
  1180.     ** a password.  Another likely input is a nudge from a counterpart
  1181.     ** on the other end, to which we respond with a prompt.
  1182.     */
  1183.     if (n < 3) {        /* Just "\r", "\n", or "\r\n" is a nudge */
  1184.         D4("Short response, %d chars.",n);
  1185.         switch (c = rsp[0]) {
  1186.         case 0x03:        /* ^C = ETX (usually means "die") */
  1187.             if (debug) P("%s: ^C in input, quitting.",getime());
  1188.             die(c);
  1189.         case '\n':        /* ^J = LF */
  1190.         case '\r':        /* ^M = CR */
  1191.         case 0x04:        /* ^D = EOT */
  1192.         case 0x02:        /* ^B = STX */
  1193.         case 0x01:        /* ^A = SOH */
  1194.             ss = S_IDLE;        /* Flush garbage from buffer */
  1195.             D4("State %d=%s",ss,gestate());
  1196.             lockwait();
  1197.             if (ibfa < ibfz) {    /* Is there input waiting? */
  1198.                 D3("Nudge plus garbage received, ignored.");
  1199.                 Resync;            /* Flush garbage from buffer */
  1200.             } else {            /* No input waiting */
  1201.                 D3("Nudged; send login prompt \"%s\"",m_login);
  1202.                 Awrite(m_login);    /* Send them a login prompt */
  1203.                 ss = S_LOGIN;        /* Note that we did it */
  1204.                 D4("State %d=%s",ss,gestate());
  1205.                 messages = 0;
  1206.             }
  1207.             Response;            /* See if there's a response */
  1208.         case 0x10:            /* This is a uucp Start-of-message */
  1209.             D3("We seem to be talking to a UUCP demon.");
  1210.             makeraw(dev);        /* Paranoia! */
  1211.             Awrite("OOOOOOOO\r");    /* Try to get it to stop */
  1212.             Resync;            /* Discard the rest of the message */
  1213.             if (m_init) Awrite(m_init);
  1214.             ss = S_INIT;        /* Try to get back to idle state */
  1215.             D4("State %d=%s",ss,gestate());
  1216.             Response;
  1217.         }
  1218.     }
  1219.     baddies = lowers = uppers = 0;/* These are for counting letters */
  1220.     D4("Scan id for char classes...");
  1221.     for (i=0; i<n; i++) {        /* Examine the response for nasty stuff */
  1222.         c = rsp[i];            /* One byte at a time */
  1223.         if (islower(c)) {
  1224.             ++lowers;            /* Lower-case letters are desirable */
  1225.             D6("c=%02X-'%c' lowers=%d",c,dsp(c),lowers);
  1226.         } else
  1227.         if (isupper(c)) {
  1228.             ++uppers;            /* Upper-case letters are acceptable */
  1229.             D6("c=%02X-'%c' uppers=%d",c,dsp(c),uppers);
  1230.         } else            /* Everything else is dubious */
  1231.         switch (c) {
  1232.         case 0x03:            /* Special goody to let others kill us */
  1233.         case 0x02:
  1234.         case 0x01:
  1235.             if (debug) P("%s: %02X in input, quitting [id]",getime(),c);
  1236.             die(c);
  1237.         case '#':            /* These are likely shell prompts */
  1238.         case '$':            /* Default Bourne shell prompt */
  1239.         case '%':            /* Default C- shell prompt */
  1240.         case '>':            /* Popular prompt in some circles */
  1241.             if (i >= n-2) {        /* Likely only if at end of input */
  1242.                 shprompt(rsp);        /* Can we handle it? */
  1243.                 Response;
  1244.             }                /* If earlier in string, reject it */
  1245.         case 0x00:
  1246.         case 0x04: case 0x05: case 0x06: case 0x07:
  1247.         case 0x08: case 0x09:            case 0x0B:
  1248.         case 0x0C:            case 0x0E: case 0x0F:
  1249.         case 0x10: case 0x11: case 0x12: case 0x13:
  1250.         case 0x14: case 0x15: case 0x16: case 0x17:
  1251.         case 0x18: case 0x19: case 0x1A: case 0x1B:
  1252.         case 0x1C: case 0x1D: case 0x1E: case 0x1F:
  1253.         case ' ' : case ':' :    /* None of these are acceptable in ids or passwords */
  1254.             D3("Invalid char %02x='%c' in input.",c,dsp(c));
  1255.             ++baddies;
  1256.             break;
  1257.         }
  1258.     }
  1259.     if (baddies || ((lowers+uppers) == 0)) {    /* Is it acceptable? */
  1260.         D4("Unacceptable; baddies=%d lowers=%d uppers=%d.",baddies,lowers,uppers);
  1261.         Resync;            /* No; drop buffered input */
  1262.         lockwait();
  1263.         if (baddies && messages > THRESH) {
  1264.             makeraw(dev);        /* Others may have munged the driver */
  1265.             if (m_init) {
  1266.                 Awrite(m_init);
  1267.                 ss = S_INIT;
  1268.                 D4("State %d=%s",ss,gestate());
  1269.             } else {
  1270.                 Awrite(m_login);
  1271.                 ss = S_LOGIN;
  1272.                 D4("State %d=%s",ss,gestate());
  1273.             }
  1274.             messages = 0;
  1275.         }
  1276.         ss = S_IDLE;
  1277.         D4("State %d=%s",ss,gestate());
  1278.         Response;
  1279.     }
  1280.     switch (ss) {        /* Special actions depending on state */
  1281.     case S_EXIT:        /* We are trying to exit */
  1282.         if (m_exit) {    /* Is there an exit command? */
  1283.             Awrite(m_exit);    /* If so, send it */
  1284.             ss = S_EXIT;    /* Note again that we're trying to quit */
  1285.             D4("State %d=%s",ss,gestate());
  1286.         }
  1287.         Response;
  1288.     case S_IDLE:        /* We're waiting for the other end to act */
  1289.         D4("Got id while idle.");
  1290.         c = rsp[0];        /* First char of response is sometimes special */
  1291.         goto maybeid;
  1292.     case S_LOGIN:        /* We just sent a login prompt */
  1293.         D4("Got response to login prompt.");
  1294.     maybeid:        /* We may have a login id */
  1295.         D4("Got possible login id.");
  1296.         if (lowers == 0) {    /* No lower-case letters; it's not an id */
  1297.             lockwait();    /* Make sure we're not interfering */
  1298.             makeraw(dev);    /* Paranoia! */
  1299.             if (m_init) {    /* Do we have a special init string? */
  1300.                 Awrite(m_init);
  1301.                 ss = S_INIT;
  1302.                 D4("State %d=%s",ss,gestate());
  1303.             }
  1304.             Response;        /* Go wait for next input */
  1305.         }
  1306.         i = checkid(rsp);    /* Put it through final tests */
  1307.         if (i <= 0) {    /* If it fails... */
  1308.             if (debug) {
  1309.                 dbgtimep = getime();
  1310.                 if (debug >= 2) Ascdnm(rsp,n,"Bad id:");
  1311.                 if (debug >= 4) Hexdnm(rsp,n,"Bad id:");
  1312.             }
  1313.             Resync;        /* Get port into known state */
  1314.             sleep(5);        /* Extra delay for safety's sake */
  1315.             ss = S_IDLE;
  1316.             D4("State %d=%s",ss,gestate());
  1317.             Response;        /* Go wait for next input */
  1318.         }
  1319.         /* It sure looks like an id */
  1320.         D3("Send password prompt \"%s\"",m_passwd);
  1321.         echoing = 0;
  1322.         Awrite(m_passwd);
  1323.         ss = S_PASSWD;
  1324.         D4("State %d=%s",ss,gestate());
  1325.         Response;
  1326.     case S_PASSWD:        /* We just sent a password prompt */
  1327.         D4("Got response to password prompt.");
  1328.         i = pswd(rsp);    /* Check it out to see if it's a good guy */
  1329.         if (i <= 0) {    /* Successful call won't return */
  1330.             D3("Unacceptable password \"%s\"",rsp);
  1331.             Response;
  1332.         }
  1333.         E("pswd(\"%s\")=%d Shouldn't happen.",rsp,i);
  1334.         Response;
  1335.     default:
  1336.         D4("ss=%d not special",ss);
  1337.     }
  1338.     if (debug) {
  1339.         dbgtimep = getime();
  1340.         if (debug >= 2) Ascdnm(rsp,n,"Ignore:");
  1341.         if (debug >= 4) Hexdnm(rsp,n,"Ignore:");
  1342.     }
  1343.     Response;
  1344. dead:  P("Giving up; %s seems to be dead.",target);
  1345.     return r;
  1346. }
  1347. \!Funky\!Stuff\!
  1348. echo file: unlock.c
  1349. cat > unlock.c << '\!Funky\!Stuff\!'
  1350. #include "uutty.h"
  1351. /*
  1352. ** Remove the lockfile.
  1353. */
  1354. unlock()
  1355. {
  1356.     D2("%s: Delete lockfile %d=\"%s\".",getime(),lockfn,lockfile);
  1357.     unlink(lockfile);
  1358.     D4("Close lockfn=%d.",lockfn);
  1359.     close(lockfn);
  1360.     lockfn = -1;
  1361.     locked = 0;
  1362. }
  1363. \!Funky\!Stuff\!
  1364.  
  1365.  
  1366.